#include"Utility.hpp"
#include"../LibDGGSKernel/IDGGSKernel.hpp"
#include"../LibDGGSBase/MathDefine.hpp"
#include<queue>
#include"fstream"
#include"json/json.h"

using namespace NNU::DGGS;
using namespace NNU::DGGS::Kernel;
/// <summary>
///单个code转为json的feature对象
/// <summary>
void code2JsonGeo(DGGSElementCode code,Json::Value &feature,DGGSGridType gridType);

/// <summary>
///生成全球格点格网的json的根节点
/// <summary>
void vertex2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType);
/// <summary>
///生成全球格边格网的json的根节点
/// <summary>
void edge2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType);
/// <summary>
///生成全球格元格网的json的根节点
/// <summary>
void cell2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType);

/// <summary>
///标记是否已查找
/// <summary>
bool isFound(std::vector< long long int> &flag, DGGSElementCode code,int level,DGGSGridType gridType,DGGSElementType elementType);
/// <summary>
///生成部分格点格网的json的根节点
/// <summary>
void partVertex2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType,double *boundBox);
/// <summary>
///生成部分格边格网的json的根节点
/// <summary>
void partEdge2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType,double *boundBox);
/// <summary>
///生成部分格元格网的json的根节点
/// <summary>
void partCell2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType,double *boundBox);



void createGlobalGrid(const char* outFileName,int level,DGGSElementType elementType,DGGSGridType gridType)
{
    std::ofstream outGrid;
    const std::string &gridName=outFileName;
    outGrid.open( gridName);
    //新版JSONCPP有些变化，需要调用它的writer来写入文件
    Json::StreamWriterBuilder builder;
    const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
    //根节点
    Json::Value jsonRoot;
    jsonRoot["type"] = "FeatureCollection";
    jsonRoot["tag"] = "NNUDGGSFileFormat";
    jsonRoot["version"] = Json::Value(1.0);
    jsonRoot["gridType"] = enum2String(gridType).c_str();
    jsonRoot["elementType"] = enum2String(elementType).c_str();
    jsonRoot["level"] = Json::Value(level);


    if (elementType == DGGSElementType::Vertex)
        vertex2JsonRoot( level,jsonRoot, gridType);
    else if (elementType == DGGSElementType::Edge)
        edge2JsonRoot( level, jsonRoot, gridType);
    else
        cell2JsonRoot( level, jsonRoot, gridType);

    writer->write(jsonRoot, &outGrid);
    jsonRoot.clear();
    outGrid.close();
}

void createPartialGrid(const char* outFileName,int level,DGGSElementType elementType,DGGSGridType gridType,double*boundBox)
{
    //写json
    std::ofstream outGrid;
    outGrid.open( outFileName);
    Json::StreamWriterBuilder builder;
    const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());

    //根节点
    Json::Value jsonRoot;
    jsonRoot["type"] = "FeatureCollection";
    jsonRoot["tag"] = "NNUDGGSFileFormat";
    jsonRoot["version"] = Json::Value(1.0);
    jsonRoot["gridType"] = enum2String(gridType).c_str();
    jsonRoot["elementType"] = enum2String(elementType).c_str();
    jsonRoot["level"] = Json::Value(level);

    //获取所有节点
    switch (elementType) {
        case DGGSElementType::Vertex:
            partVertex2JsonRoot( level,jsonRoot, gridType,boundBox);
            break;
        case DGGSElementType::Edge:
            partEdge2JsonRoot( level,jsonRoot, gridType, boundBox);
            break;
        case DGGSElementType::Cell:
            partCell2JsonRoot( level, jsonRoot, gridType,boundBox);
            break;
        default:
            break;
    }

    //写入文件
    writer->write(jsonRoot, &outGrid);
    jsonRoot.clear();
    outGrid.close();
}



// code转为json对象
void code2JsonGeo(DGGSElementCode code,Json::Value &feature,DGGSGridType gridType)
{
    IDGGSKernel *pKernel= CreateKernelObj(gridType);
    switch (code.elementType) {
        case DGGSElementType::Vertex:
        {
            GeoCoord coord{};
            pKernel->decode(code, coord);
            Json::Value geometry;
            geometry["type"] = "point";
            geometry["coordinates"].append(Json::Value((double)coord.lon));
            geometry["coordinates"].append(Json::Value((double)coord.lat));
            geometry["coordinates"].append(Json::Value(0));
            feature["geometry"] = geometry;
            Json::Value properties;
            properties["id"] = Json::Value(code2Id(code,gridType));
            feature["properties"]=properties;
            feature["type"] = "Feature";
            break;
        }
        case DGGSElementType::Edge:
        {
            DGGSElementCodeIter*iter= nullptr;
            int vertexesNum;
            pKernel->query(DGGSTopoType::RelativeVertexes, code,iter,vertexesNum);
            Json::Value geometry;
            Json::Value point;
            for (int i = 0; i < 2; i++)
            {
                GeoCoord coord{};
                pKernel->decode(iter[i].code,coord);
                point["point"].append(Json::Value((double)coord.lon));
                point["point"].append(Json::Value((double)coord.lat));
                point["point"].append(Json::Value(0));
                point["coordinate"].append(point["point"]);
                point["point"].clear();
            }
            delete[]iter;
            geometry["coordinates"]=point["coordinate"];
            geometry["type"] = "LineString";
            feature["geometry"] = geometry;
            Json::Value properties;
            properties["id"] = Json::Value(code2Id(code,gridType));
            feature["properties"]=properties;
            feature["type"] = "Feature";
            break;
        }
        case DGGSElementType::Cell:
        {
            DGGSElementCodeIter*iter= nullptr;
            int vertexesNum;
            pKernel->query(DGGSTopoType::RelativeVertexes, code,iter,vertexesNum);
            Json::Value geometry;
            Json::Value point;
            for (int i = 0; i < vertexesNum; i++)
            {
                GeoCoord coord{};
                pKernel->decode(iter[i].code,coord);
                point["point"].append(Json::Value((double)coord.lon));
                point["point"].append(Json::Value((double)coord.lat));
                point["point"].append(Json::Value(0));
                point["coordinate"].append(point["point"]);
                point["point"].clear();
            }
            delete[]iter;
            geometry["coordinates"].append(point["coordinate"]);
            geometry["type"] = "Polygon";
            feature["geometry"] = geometry;
            Json::Value properties;
            properties["id"] = Json::Value(code2Id(code,gridType));
            feature["properties"]=properties;
            feature["type"] = "Feature";
            break;
        }
        default:
            break;
    }
    DestroyKernelObj(pKernel);
}

// 生成全球的格点的json的root
void vertex2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType)
{
    long long rowMax= rowMaxSize(level);
    long long vMaxCol= vColMaxSize(level,gridType);
    Json::Value feature;

    DGGSElementCode code;
    code.elementType=DGGSElementType::Vertex;
    code.level=level;

    for (int q = 0; q < 12; q++)
    {
        if (q == 0||q == 11)
        {
            for (int v = 0; v < 5; v++)
            {
                code.basePartition=q;
                code.row=0;
                code.col=v;
                code2JsonGeo(code,feature,gridType);
                jsonRoot["features"].append(feature);
                feature.clear();
                //三角形、菱形的0,11只有一个节点，六边形有5个
                if(gridType!=DGGSGridType::DGGS_ISEA4H)
                    break;
            }
        }
        else
        {
            //其余菱形块
            for (int i = 0; i < rowMax; i++)
            {
                for (int j = 0; j < vMaxCol; j++)
                {
                    //六边形的第一列节点数少一
                    if(gridType==DGGSGridType::DGGS_ISEA4H&&j==vMaxCol-1&&i==0)
                        break;
                    code.basePartition=q;
                    code.row=i;
                    code.col=j;
                    code2JsonGeo(code,feature,gridType);
                    jsonRoot["features"].append(feature);
                    feature.clear();
                }
            }
        }
    }
}
// 生成全球的格边的json的root
void edge2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType)
{
    long long rowMax= rowMaxSize(level);
    long long eMaxCol= eColMaxSize(level,gridType);
    Json::Value feature;

    //三角形、菱形只在1-10有边要素，六边形格网在0-11有边要素
    int firBasePartition=0;
    int lastBasePartition=baseDiamondCount+1;
    if(gridType!=DGGSGridType::DGGS_ISEA4H)
    {
        firBasePartition=1;
        lastBasePartition=baseDiamondCount;
    }

    DGGSElementCode code;
    code.elementType=DGGSElementType::Edge;
    code.level=level;

    for (int q = firBasePartition; q <= lastBasePartition; q++)
    {
        //北极点
        if (q == 0||q==11)
        {
            for (int e = 0; e < 5; e++)
            {
                code.basePartition=q;
                code.row=0;
                code.col=e;
                code2JsonGeo(code,feature,gridType);
                jsonRoot["features"].append(feature);
                feature.clear();
            }
        }
        else
        {
            //其余菱形块
            for (int i = 0; i < rowMax; i++)
            {
                for (int j = 0; j < eMaxCol; j++)
                {
                    //六边形格网第一列边数少一
                    if(gridType==DGGSGridType::DGGS_ISEA4H&&j==eMaxCol-1&&i==0)
                        continue;

                    code.basePartition=q;
                    code.row=i;
                    code.col=j;
                    code2JsonGeo(code,feature,gridType);
                    jsonRoot["features"].append(feature);
                    feature.clear();
                }
            }
        }
    }
}
// 生成全球的格元的json的root
void cell2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType)
{
    long long rowMax= rowMaxSize(level);
    long long fMaxCol= fColMaxSize(level,gridType);
    Json::Value feature;

    //三角形、菱形只在1-10有面要素，六边形格网在0-11有面要素
    int firBasePartition=0;
    int lastBasePartition=baseDiamondCount+1;
    if(gridType!=DGGSGridType::DGGS_ISEA4H)
    {
        firBasePartition=1;
        lastBasePartition=baseDiamondCount;
    }


    DGGSElementCode code;
    code.elementType=DGGSElementType::Cell;
    code.level=level;

    for (int q = firBasePartition; q <= lastBasePartition; q++)
    {
        //北极点
        if (q == 0||q == 11)
        {
            code.basePartition=q;
            code.row=0;
            code.col=0;
            code2JsonGeo(code,feature,gridType);
            jsonRoot["features"].append(feature);
            feature.clear();

        }
        else
        {
            //其余菱形块
            for (int i = 0; i < rowMax; i++)
            {
                for (int j = 0; j < fMaxCol; j++)
                {
                    code.basePartition=q;
                    code.row=i;
                    code.col=j;
                    code2JsonGeo(code,feature,gridType);
                    jsonRoot["features"].append(feature);
                    feature.clear();
                }
            }
        }
    }
}

// 已找格元的标记和判断
bool isFound(std::vector< long long int> &flag, DGGSElementCode code,int level,DGGSGridType gridType,DGGSElementType elementType) {
    auto maxRow=(long long int)pow(2,level);
    long long int maxCol=0;
    switch (elementType) {
        case DGGSElementType::Edge:
            maxCol=eColMaxSize(level,gridType);
            break;
        case DGGSElementType::Vertex:
            maxCol=vColMaxSize(level,gridType);
            break;
        case DGGSElementType::Cell:
            maxCol=fColMaxSize(level,gridType);
            break;
        default:break;
    }

    long long int label=code.basePartition*maxRow*maxCol+code.row*maxCol+code.col;
    auto it = std::find(flag.begin(), flag.end(), label);
    if (it != flag.end())//找到
        return true;
    else//没找到
    {
        flag.push_back(label);
        return false;
    }
}
// 生成局部的格点的json的root
void partVertex2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType,double *boundBox)
{
    auto rangePolygon=new GeoCoord[5];
    rangePolygon[0]=GeoCoord{boundBox[0], boundBox[2]};
    rangePolygon[1]=GeoCoord{boundBox[1], boundBox[2]};
    rangePolygon[2]=GeoCoord{boundBox[1], boundBox[3]};
    rangePolygon[3]=GeoCoord{boundBox[0], boundBox[3]};
    rangePolygon[4]=GeoCoord{boundBox[0], boundBox[2]};

    IDGGSKernel*pKernel= CreateKernelObj(gridType);
    Json::Value feature;

    //获取种子节点
    DGGSElementCode seekCode;
    seekCode.elementType=DGGSElementType::Vertex;
    seekCode.level=level;

    GeoCoord seekCoord{(boundBox[0] + boundBox[1]) / 2, (boundBox[2] + boundBox[3]) / 2};
    pKernel->encode(seekCoord,seekCode);

    //队列获取范围内所有格网
    std::queue<DGGSElementCode> codeQue;
    std::vector<long long int> flag;
    codeQue.push(seekCode);

    while(!codeQue.empty())
    {
        DGGSElementCode code= codeQue.front();
        //写入jsonRoot
        feature.clear();
        code2JsonGeo(code,feature,gridType);
        jsonRoot["features"].append(feature);

        //标记已找过
        codeQue.pop();
        isFound(flag, code,level,gridType,DGGSElementType::Vertex);

        //获取相邻要素
        DGGSElementCodeIter *codeIter = nullptr;
        int codeNum;
        DGGSTopoType topoType=DGGSTopoType::RelativeVertexes;
        pKernel->query(topoType, code, codeIter, codeNum);

        //判断是否加入结果
        for (int k = 0; k < codeNum; k++)
        {
            GeoCoord coord{0, 0};
            pKernel->decode(codeIter[k].code, coord);
            if (isPointInPolygon(rangePolygon, 5, coord) && !isFound(flag, codeIter[k].code, level, gridType, DGGSElementType::Vertex))
                codeQue.push(codeIter[k].code);
        }
        delete[]codeIter;
    }

    DestroyKernelObj(pKernel);
}
// 生成局部的格边的json的root
void partEdge2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType,double *boundBox)
{
    auto rangePolygon=new GeoCoord[5];
    rangePolygon[0]=GeoCoord{boundBox[0], boundBox[2]};
    rangePolygon[1]=GeoCoord{boundBox[1], boundBox[2]};
    rangePolygon[2]=GeoCoord{boundBox[1], boundBox[3]};
    rangePolygon[3]=GeoCoord{boundBox[0], boundBox[3]};
    rangePolygon[4]=GeoCoord{boundBox[0], boundBox[2]};

    IDGGSKernel*pKernel= CreateKernelObj(gridType);
    Json::Value feature;

    //获取种子节点
    DGGSElementCode seekCode;
    seekCode.elementType=DGGSElementType::Edge;
    seekCode.level=level;

    GeoCoord seekCoord{(boundBox[0] + boundBox[1]) / 2, (boundBox[2] + boundBox[3]) / 2};
    pKernel->encode(seekCoord,seekCode);

    //队列获取范围内所有格网
    std::queue<DGGSElementCode> codeQue;
    std::vector<long long int> flag;
    codeQue.push(seekCode);

    while(!codeQue.empty())
    {
        DGGSElementCode code= codeQue.front();
        //写入jsonRoot
        feature.clear();
        code2JsonGeo(code,feature,gridType);
        jsonRoot["features"].append(feature);

        //标记已找过
        codeQue.pop();
        isFound(flag, code,level,gridType,DGGSElementType::Edge);

        //获取相邻要素
        DGGSElementCodeIter *codeIter = nullptr;
        int codeNum;
        DGGSTopoType topoType=DGGSTopoType::RelativeEdges;
        pKernel->query(topoType, code, codeIter, codeNum);

        //判断是否加入结果
        for (int k = 0; k < codeNum; k++)
        {
            GeoCoord coord{0, 0};
            pKernel->decode(codeIter[k].code, coord);
            if (isPointInPolygon(rangePolygon, 5, coord) && !isFound(flag, codeIter[k].code, level, gridType, DGGSElementType::Edge))
                codeQue.push(codeIter[k].code);
        }
        delete[]codeIter;
    }

    DestroyKernelObj(pKernel);
}
// 生成局部的格元的json的root
void partCell2JsonRoot(int level, Json::Value &jsonRoot,DGGSGridType gridType,double *boundBox)
{
    auto rangePolygon=new GeoCoord[5];
    rangePolygon[0]=GeoCoord{boundBox[2], boundBox[0]};
    rangePolygon[1]=GeoCoord{boundBox[2], boundBox[1]};
    rangePolygon[2]=GeoCoord{boundBox[3], boundBox[1]};
    rangePolygon[3]=GeoCoord{boundBox[3], boundBox[0]};
    rangePolygon[4]=GeoCoord{boundBox[2], boundBox[0]};

    IDGGSKernel*pKernel= CreateKernelObj(gridType);
    Json::Value feature;

    //获取种子节点
    DGGSElementCode seekCode;
    seekCode.elementType=DGGSElementType::Cell;
    seekCode.level=level;

    GeoCoord seekCoord{(boundBox[2] + boundBox[3]) / 2, (boundBox[1] + boundBox[0]) / 2};
    pKernel->encode(seekCoord,seekCode);

    //队列获取范围内所有格网
    std::queue<DGGSElementCode> codeQue;
    std::vector<long long int> flag;
    codeQue.push(seekCode);

    while(!codeQue.empty())
    {
        DGGSElementCode code= codeQue.front();
        //写入jsonRoot
        feature.clear();
        code2JsonGeo(code,feature,gridType);
        jsonRoot["features"].append(feature);

        //标记已找过
        codeQue.pop();
        isFound(flag, code,level,gridType,DGGSElementType::Cell);

        //获取相邻要素
        DGGSElementCodeIter *codeIter = nullptr;
        int codeNum;
        DGGSTopoType topoType=DGGSTopoType::RelativeCells;
        pKernel->query(topoType, code, codeIter, codeNum);

        //判断是否加入结果
        for (int k = 0; k < codeNum; k++)
        {
            GeoCoord coord{0, 0};
            pKernel->decode(codeIter[k].code, coord);
            if (isPointInPolygon(rangePolygon, 5, coord) && !isFound(flag, codeIter[k].code, level, gridType, DGGSElementType::Cell))
                codeQue.push(codeIter[k].code);
        }
        delete[]codeIter;
    }

    DestroyKernelObj(pKernel);
}







